/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { Target } from '@ohos/hvigor-ohos-plugin'
import { ModuleBuildProfile } from '@ohos/hvigor-ohos-plugin/src/options/build/module-build-profile'
import { PathConstants} from '../../../core';
import { IModuleContext } from '../IModuleContext'
import { ITargetContext } from '../ITargetContext'
import SerializationPathUtil from '../../../core/utils/SerializationPathUtil';
import { CustomError, ErrorCodes } from '../../../core/utils/CustomError';

export class TargetContextImpl implements ITargetContext {
  readonly target: Target
  readonly context: IModuleContext

  private readonly DEFAULT_SOURCE_ROOT = PathConstants.DEFAULT_SOURCE_ROOT
  private readonly GENERATED_ROOT = PathConstants.GENERATED_ROOT

  constructor(target: Target, context: IModuleContext) {
    this.target = target
    this.context = context
  }

  private get targetOpt(): ModuleBuildProfile.ModuleTargetBuildOpt {
    return this.context.buildProfileOpt.targets?.find(
      (item: ModuleBuildProfile.ModuleTargetBuildOpt) => item.name === this.target.getTargetName())!;
  }

  /**
   * 获取当前目标的扫描文件
   * 从所有扫描文件中筛选出属于当前目标源代码根目录的文件
   *
   * @returns 当前目标的扫描文件路径数组，只包含属于当前目标源代码根目录的文件
   */
  getCurrentTargetScanFiles(): string[] {
    // 获取当前目标的所有源代码根目录的绝对路径
    const absoluteSourceRoots = this.getAbsoluteSourceRoots()

    // 过滤扫描文件，只保留在当前目标源代码根目录下的文件
    return this.context.scanFiles.filter(item => {
      // 标准化文件路径格式，确保路径分隔符统一
      const normalizedFilePath = SerializationPathUtil.normalize(item)

      // 检查文件路径是否以任一源代码根目录开头
      // 这样可以确保只处理属于当前目标的文件
      return absoluteSourceRoots.some(sourceRoot => normalizedFilePath.startsWith(sourceRoot))
    })
  }

  getModuleName(): string {
    return this.context.moduleContext.getModuleName()
  }

  getModulePath(): string {
    return this.context.moduleContext.getModulePath();
  }

  getPackageName(): string {
    // 这里需要获取oh-package.json5中配置的name
    const packagePath = SerializationPathUtil.pathResolve(this.getModulePath(), 'oh-package.json5')
    const ohPackageObj = SerializationPathUtil.readJson5(packagePath)
    return ohPackageObj.name
  }

  mapToOutputPath(absSourceFile: string): string {
    const normalizedFilePath = SerializationPathUtil.normalize(absSourceFile)
    const moduleRoot = this.context.moduleContext.getModulePath()
    const srcMainAbs = SerializationPathUtil.normalize(SerializationPathUtil.pathResolve(moduleRoot, this.DEFAULT_SOURCE_ROOT))
    const generatedRootAbs = this.getOutputRoot()

    // 优先基于 src/main 做路径映射
    if (normalizedFilePath.startsWith(srcMainAbs + SerializationPathUtil.sep)) {
      const relativeToSrcMain = SerializationPathUtil.relative(srcMainAbs, normalizedFilePath)
      return SerializationPathUtil.join(generatedRootAbs, relativeToSrcMain)
    }

    // 如果文件不在 src/main 下，退化为：找到匹配的任一 sourceRoot，映射到 generated 根下
    const absoluteSourceRoots = this.getAbsoluteSourceRoots()
    const matchedRoot = absoluteSourceRoots.find(root => normalizedFilePath.startsWith(root))
    if (matchedRoot) {
      const rel = SerializationPathUtil.relative(matchedRoot, normalizedFilePath)
      return SerializationPathUtil.join(generatedRootAbs, rel)
    }

    // 否则直接放在 generated 根下，保持文件名
    return SerializationPathUtil.join(generatedRootAbs, SerializationPathUtil.basename(normalizedFilePath))
  }

  getOutputRoot(): string {
    const moduleRoot = this.context.moduleContext.getModulePath()
    const absolutePath = SerializationPathUtil.pathResolve(moduleRoot, this.GENERATED_ROOT)
    return SerializationPathUtil.normalize(absolutePath)
  }

  getRelativePath(absolutePath: string): string {
    const outputRoot = this.getOutputRoot()
    // 从输出根目录推断项目根目录
    // 假设输出根目录为: <module>/src/generated
    const moduleRoot = SerializationPathUtil.dirname(SerializationPathUtil.dirname(outputRoot))

    try {
      return SerializationPathUtil.relative(moduleRoot, absolutePath).replaceAll(SerializationPathUtil.sep, '/')
    } catch (error) {
      // 如果相对路径计算失败，返回原路径
      return absolutePath
    }
  }

  isValidOutputPath(outputPath: string): boolean {
    try {
      const outputRoot = this.getOutputRoot()
      const resolvedOutputPath = SerializationPathUtil.pathResolve(outputPath)
      const resolvedOutputRoot = SerializationPathUtil.pathResolve(outputRoot)

      // 检查输出路径是否在输出根目录下
      return resolvedOutputPath.startsWith(resolvedOutputRoot)
    } catch (error) {
      return false
    }
  }

  ensureOutputDirectory(outputPath: string): void {
    SerializationPathUtil.ensureDirSync(SerializationPathUtil.dirname(outputPath))
  }

  calculateSourceRootToModuleRoot(absolutePath: string): string {
    // 处理Windows环境路径兼容性
    const normalizedAbsolutePath = absolutePath.replaceAll(SerializationPathUtil.sep, '/')

    // 获取所有源根路径并标准化为统一格式（以 / 结尾）
    const sourceRoots = this.getAbsoluteSourceRoots().map(root =>
      root.replaceAll(SerializationPathUtil.sep, '/').replace(/\/$/, '') + '/'
    )

    // 找到匹配的源根路径
    const sourceRoot = sourceRoots.find(root => normalizedAbsolutePath.startsWith(root))

    if (!sourceRoot) {
      throw new CustomError(`无法找到匹配的源根路径: ${normalizedAbsolutePath}。可用的源根路径: ${sourceRoots.join(', ')}`, ErrorCodes.ROOT_DIRECTROY_NOT_FOUND)
    }

    // 计算相对路径，移除源根路径的尾部分隔符进行计算
    const sourceRootForRelative = sourceRoot.replace(/\/$/, '')
    const absolutePathForRelative = normalizedAbsolutePath.replace(/\/$/, '')

    return SerializationPathUtil.relative(sourceRootForRelative, absolutePathForRelative).replaceAll(SerializationPathUtil.sep, '/')
  }

  private get targetSourceRoots(): string[] {
    if (!this.targetOpt.source?.sourceRoots) {
      return [this.DEFAULT_SOURCE_ROOT]
    }
    // 检查默认路径是否已包含在sourceRoots中
    if (!this.targetOpt.source.sourceRoots.includes(this.DEFAULT_SOURCE_ROOT)) {
      return [this.DEFAULT_SOURCE_ROOT, ...this.targetOpt.source.sourceRoots]
    }
    return this.targetOpt.source.sourceRoots
  }

  /**
   * 获取当前目标的源代码根目录的绝对路径
   * 将配置的相对路径源代码根目录转换为标准化的绝对路径
   *
   * @returns 标准化的绝对路径数组，每个路径都以路径分隔符结尾
   */
  private getAbsoluteSourceRoots(): string[] {
    // 获取当前模块的根目录路径
    const moduleRoot = this.context.moduleContext.getModulePath()

    // 将每个相对路径源代码根目录转换为绝对路径
    return this.targetSourceRoots.map((sourceRoot: string) => {
      // 将相对路径解析为基于模块根目录的绝对路径
      // path.resolve 会处理相对路径符号（如 ../ ./）
      const absolutePath = SerializationPathUtil.pathResolve(moduleRoot, sourceRoot)

      // 标准化路径格式，确保路径分隔符统一，处理多余的分隔符
      const normalizedPath = SerializationPathUtil.normalize(absolutePath)

      // 确保路径以分隔符结尾，避免路径匹配错误
      // 例如：/project/src 不会错误匹配 /project/src2
      return normalizedPath.endsWith(SerializationPathUtil.sep) ? normalizedPath : normalizedPath + SerializationPathUtil.sep
    })
  }
}
